package com.caishi.lkx.exam.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.caishi.lkx.common.ienum.ClientType;
import com.caishi.lkx.common.util.IndexUtil;
import com.caishi.lkx.exam.mapper.CategoryMapper;
import com.caishi.lkx.exam.model.*;
import com.caishi.lkx.exam.service.*;
import com.caishi.lkx.exam.vo.SpecialConditionVo;
import com.caishi.lkx.user.service.IGroupService;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBaseMapper;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.entity.impl.BaseIntEntity;
import com.zzw.common.exception.BizException;
import com.zzw.common.exception.BizRuntimeException;

import com.caishi.lkx.exam.ienum.type.ColumnType;
import com.caishi.lkx.exam.ienum.type.QuestionSourceType;

import com.caishi.lkx.exam.model.re.ExColumnConfigReModel;

import com.caishi.lkx.order.IAssetsModel;
import com.caishi.lkx.order.ienum.status.AssetsStatus;
import com.caishi.lkx.order.ienum.type.AssetsType;
import com.caishi.lkx.order.model.OrderModel;
import com.caishi.lkx.order.model.UserAssetsDataModel;
import com.caishi.lkx.order.service.IUserAssetsDataService;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.zzw.common.cache.CacheConstants.BIG_UP_UP;

@Service
//@DubboService(interfaceClass = ICategoryBaseService.class)
public class CategoryServiceImpl implements ICategoryService {

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private IColumnService columnService;

    @Resource
    private IChapterService chapterService;

    @Resource
    private IPaperService paperService;

    @Resource
    private IQuestionService questionService;

    @Resource
    private IUserAssetsDataService userAssetsDataService;

    @Resource
    private IGroupService groupService;

    @Resource
    private IQuestionTypeService questionTypeService;

    @Resource
    private IExColumnConfigReService exColumnConfigReService;

    @Override
    public IBaseMapper<CategoryModel, Long> getMapper() {
        return categoryMapper;
    }

    @Override
    @Cacheable(value = BIG_UP_UP, key = "'tree:category:' + #parent.id + #n", unless = "#result == null")
    public CategoryModel setChildren(CategoryModel parent, int n) {
        return ICategoryService.super.setChildren(parent, n);
    }

    @Override
    public CategoryModel builder() {
        return new CategoryModel();
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    public CategoryModel insert(CategoryModel entity) {
        // 设置科目为叶子节点
        entity.setAttribute(entity.getAttribute() | 1);
        if (entity.getParentId() != null) {
            categorySetParent(entity.getParentId(), true);
        }
        return ICategoryService.super.insert(entity);
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public CategoryModel update(CategoryModel entity) {
        CategoryModel old = getById(entity.getId());
        if (entity.getParentId() != null) {
            if (!entity.getParentId().equals(old.getParentId())) {
                categorySetParent(entity.getParentId(), true);
                if (old.getParentId() != null) {
                    categorySetParent(old.getParentId(), false);
                }
            }
        }
        if (StrUtil.isNotBlank(entity.getExamId()) && !old.getExamId().equals(entity.getExamId())) {
            var childrenIds = allChildrenIds(entity.getId());
            var parentId = entity.getParentId();
            if (!parentIdIsNull(parentId)) {
                getMapper().update(null, Wrappers.<CategoryModel>lambdaUpdate()
                        .setSql("attribute = attribute | 1")
                        .eq(BaseIntEntity::getId, parentId)
                );
            }
            for (var id : childrenIds) {
                var u = new CategoryModel();
                u.setId(id);
                u.setExamId(entity.getExamId());
                update(u);
            }
        }
        return ICategoryService.super.update(entity);
    }

    @Override
    public CategoryModel simpleUpdate(CategoryModel entity) {
        return owner(ICategoryService.class).update(entity);
    }

    @Override
    public boolean update(CategoryModel entity, Wrapper<CategoryModel> updateWrapper) {
        return getMapper().update(entity, updateWrapper) == 1;
        //throw new BizRuntimeException();
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    public Boolean delete(Long aLong) {
        return ICategoryService.super.delete(aLong);
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    public Boolean delete(List<Long> longs) {
        return ICategoryService.super.delete(longs);
    }

    @Override
    public int delete(UpdateWrapper<CategoryModel> wrapper) {
        throw new BizRuntimeException();
    }

    @Override
    public int delete(LambdaUpdateWrapper<CategoryModel> wrapper) {
        throw new BizRuntimeException();
    }

    @Override
    public List<String> getNameListByIds(List<Long> categoryIds) {
        if (CollUtil.isNotEmpty(categoryIds)) {
            return this.selectBatchIds(categoryIds).stream().map(CategoryModel::getName).toList();
        }
        return Collections.emptyList();
    }

    @Override
    public boolean haveColumn(Long categoryId) {
        return columnService.selectCount(Wrappers.<ColumnModel>lambdaQuery().eq(ColumnModel::getCategoryId, categoryId)) > 0;
    }

    @Override
    public int fetchCategoryQuestionCount(Long categoryId) {
        var columnIds = columnService.categoryColumn(categoryId, ColumnType.chapterPractice);
        List<String> paperColumnIds = columnService.categoryColumn(categoryId, ColumnType.paperLibrary);
        Integer paperSize = columnService.fetchQuestionSize(paperColumnIds, ColumnType.paperLibrary);
        Integer questionSize = columnService.fetchQuestionSize(columnIds, ColumnType.chapterPractice);
        return paperSize + questionSize;
    }

    @Override
    public int allQuestionCount(Long categoryId) {
        return Math.toIntExact(questionService.selectCount(questionService.wrappers().eq(QuestionModel::getCategoryId, categoryId)));
    }

    @Override
    public Boolean canDelete(List<Long> keys) {
        if (columnService.selectCount(columnService.wrappers().in(keys.size() > 1, ColumnModel::getCategoryId, keys)
                .eq(keys.size() == 1, ColumnModel::getCategoryId, keys.get(0))
        ) > 0) {
            throw new BizRuntimeException("请先移除科目下的栏目");
        }
        if (chapterService.selectCount(chapterService.wrappers().in(keys.size() > 1, ChapterModel::getCategoryId, keys)
                .eq(keys.size() == 1, ChapterModel::getCategoryId, keys.get(0))
        ) > 0) {
            throw new BizRuntimeException("请先移除科目下的章节");
        }

        if (paperService.selectCount(paperService.wrappers().in(keys.size() > 1, PaperModel::getCategoryId, keys)
                .eq(keys.size() == 1, PaperModel::getCategoryId, keys.get(0))
        ) > 0) {
            throw new BizRuntimeException("请先移除科目下的试卷");
        }
        //  判断科目下是否有科目试卷规则
//        if (paperRuleService.selectCount(paperRuleService.wrappers().apply("")) > 0) {
//            throw new BizRuntimeException("请先移除科目下的试卷规则");
//        }
        return true;
    }

    private void categorySetParent(Long id, boolean parent) {
        if (0 == id) return;
        if (!parent) {
            if (selectCount(wrappers().eq(CategoryModel::getParentId, id)) > 1) {
                return;
            }
        }
        categoryMapper.update(null, Wrappers.<CategoryModel>lambdaUpdate()
                .setSql(parent, "attribute = attribute & 0xfffffffe")
                .setSql(!parent, "attribute = attribute | 1")
                .eq(CategoryModel::getId, id)

        );
    }

    @Override
    public boolean currentHaveAuth(Long userGroupId, Long localGroupId, String userId, Long categoryId, ClientType clientType) throws BizException {
        var auth = userAssetsDataService.userHaveAsset(userId, AssetsType.category, categoryId.toString());
        //|| groupService.groupsHaveCategoryPermission(userGroupId, StrUtil.isNotBlank(userId) ? null : localGroupId, categoryId, clientType)
        return auth ;
    }

    @Override
    public List<Long> userHaveCategory(String userId) {
        if (StrUtil.isBlank(userId)) return new LinkedList<>();
        return userAssetsDataService.userAsset(userId, AssetsType.category)
                .stream().map(v -> Long.valueOf(v.getAssetMappingId())).distinct().collect(Collectors.toList());
    }

    @Override
    public List<Long> userHaveCategoryTree(String userId) {
        var leaf = userHaveCategory(userId);
        leaf.addAll(leaf.stream().parallel().flatMap(v -> allParentIds(v).stream()).distinct().toList());
        return leaf;
    }

    @Override
    public List<String> categoryRelationQuestionIds(Long categoryId) {
        var models = columnService.selectList(columnService.wrappers()
                .select(ColumnModel::getId, ColumnModel::getType)
                .eq(ColumnModel::getCategoryId, categoryId)
                .in(ColumnModel::getType, ColumnType.chapterPractice, ColumnType.paperLibrary)
        );
        var chapterIds = models.stream().filter(v -> v.getType() == ColumnType.chapterPractice)
                .map(BaseEntity::getId)
                .toList();
        var paperIds = models.stream().filter(v -> v.getType() == ColumnType.paperLibrary)
                .map(BaseEntity::getId)
                .toList();
        List<String> list = new LinkedList<>();
        list.addAll(columnService.fetchQuestionIds(chapterIds, ColumnType.chapterPractice));
        list.addAll(columnService.fetchQuestionIds(paperIds, ColumnType.paperLibrary));
        return list.parallelStream().distinct().toList();
    }

    @Override
    public SpecialConditionVo categoryGenerateCollectCondition(Long categoryId, List<String> inIds) {
        var vo = new SpecialConditionVo();
        if (categoryId == null && CollUtil.isEmpty(inIds)) return vo;
        vo.setMaxCount(0);
        vo.setTypes(new LinkedList<>());
        Collection<QuestionModel> allTypeIds;
        if (CollUtil.isNotEmpty(inIds)) {
            List<List<String>> split = IndexUtil.splitList(inIds);
            allTypeIds = split.parallelStream().map(v -> questionService.selectList(Wrappers.<QuestionModel>query()
                    .select("type_id", "first_type", "count(*) as knowledge_level")
                    .in("id", v)
                    .ne("source_type", QuestionSourceType.combination)
                    .eq(categoryId != null, "category_id", categoryId)
                    .groupBy("type_id", "first_type")
            )).flatMap(List::stream).collect(Collectors.toMap(QuestionModel::getTypeId, v -> v, (v, v1) -> {
                v.setKnowledgeLevel(v.getKnowledgeLevel() + v1.getKnowledgeLevel());
                return v;
            })).values();
        } else {
            allTypeIds = questionService.selectList(Wrappers.<QuestionModel>query()
                    .select("type_id", "first_type", "count(*) as knowledge_level")
                    .ne("source_type", QuestionSourceType.combination)
                    .eq("category_id", categoryId)
                    .groupBy("type_id", "first_type")
            );
        }
        var result = allTypeIds.stream().map(v -> {
            var k = new SpecialConditionVo.TY();
            k.setName(questionTypeService.queryName(v.getTypeId(), v.getFirstType()));
            k.setTypeId(v.getTypeId());
            k.setType(v.getFirstType());
            k.setCount(v.getKnowledgeLevel());
            return k;
        }).toList();
        return new SpecialConditionVo(result.stream().mapToInt(SpecialConditionVo.TY::getCount).sum(), result);
    }

    @Override
    public List<CategoryModel> filteringPaper(List<CategoryModel> list, Boolean onlyLeaf) {
        return list.parallelStream().filter(f -> {
            AtomicReference<Boolean> filter = new AtomicReference<>(false);
            boolean haveChild = f.getHaveChild();
            if (onlyLeaf || !haveChild) {
                List<String> l = columnService.categoryColumn(f.getId(), ColumnType.paperLibrary);
                l.parallelStream().forEach(e -> {
                    Long aLong = exColumnConfigReService.selectCount(Wrappers.<ExColumnConfigReModel>lambdaQuery().eq(ExColumnConfigReModel::getColumnId, e));
                    if (aLong > 0) filter.set(true);
                });
            }

            if (haveChild) {
                List<Long> child = new ArrayList<>();
                childCategory(list, child, f.getId());
                child.parallelStream().forEach(e -> {
                    Long aLong = exColumnConfigReService.selectCount(Wrappers.<ExColumnConfigReModel>lambdaQuery().eq(ExColumnConfigReModel::getColumnId, e));
                    if (aLong > 0) {
                        filter.set(true);
                    }
                });
            }
            return filter.get();
        }).collect(Collectors.toList());
    }

    @Override
    public List<String> categoryByLeafPath(Long cid) {
        List<String> path = new ArrayList<>();
        CategoryModel categoryModel = this.getById(cid);
        path.add(categoryModel.getName());
        while (null != categoryModel.getParentId() && categoryModel.getParentId() != 0L) {
            CategoryModel model = this.getById(categoryModel.getParentId());
            path.add(0, model.getName());
            categoryModel = model;
        }
        return path;
    }

    @Override
    public List<String> columnRelationCollectionByCategory(Long categoryId, ColumnType columnType) {
        List<String> columnIds = columnService.categoryColumn(categoryId, columnType);
        return columnService.columnRelationCollectionIds(columnIds);
    }

    private void childCategory(List<CategoryModel> list, List<Long> child, Long pid) {
        list.parallelStream().filter(f -> f.getParentId().equals(pid)).forEach(p -> {
            if (p.getHaveChild()) {
                this.childCategory(list, child, p.getParentId());
            } else {
                child.add(p.getId());
            }
        });

    }

    @Override
    public String orderOnlinePayTitle(OrderModel orderModel, String id) {
        return getById(Long.valueOf(id)).getServerName();
    }


    @Override
    public UserAssetsDataModel assetsIssuePreHandler(OrderModel orderModel, UserAssetsDataModel userAssetsDataModel) {
        var old = userAssetsDataService.userAsset(userAssetsDataModel.getUserId(), userAssetsDataModel.getAssetType(), userAssetsDataModel.getAssetMappingId(), AssetsStatus.effective);
        if (old.isEmpty()) return userAssetsDataModel;
        var f = old.get(0);
        f.setEndTime(userAssetsDataModel.getEndTime().isAfter(f.getEndTime()) ? userAssetsDataModel.getEndTime() : f.getEndTime());
        return f;
    }

    @Override
    public void assetsRefundCall(OrderModel orderModel, UserAssetsDataModel userAssetsDataModel) {
        // 过期后清除做题记录
    }

    @Override
    public IAssetsModel<CategoryModel, Long> assetsDetail(String id) {
        return getById(Long.valueOf(id));
    }

//    @Override
//    public List<RpcCategoryModel> rpcGetList(String examId) {
//        List<CategoryModel> categoryModels = categoryMapper.selectList(Wrappers.<CategoryModel>lambdaQuery().eq(CategoryModel::getExamId, examId));
//        if (!categoryModels.isEmpty()) {
//            return categoryModels.stream().map(v -> BeanUtil.copyProperties(v, RpcCategoryModel.class)).collect(Collectors.toList());
//        } else {
//            return null;
//        }
//    }
//
//    @Override
//    public List<RpcCategoryModel> rpcGetListByIds(List<Integer> categroyIds) {
//        if (null == categroyIds || categroyIds.isEmpty()) {
//            return null;
//        }
//        List<CategoryModel> categoryModels = this.selectList(Wrappers.<CategoryModel>lambdaQuery()
//                .in(CategoryModel::getId, categroyIds).orderByAsc(CategoryModel::getSort));
//        return categoryModels.stream().map(v -> BeanUtil.copyProperties(v, RpcCategoryModel.class)).collect(Collectors.toList());
//    }
//
//    @Override
//    public RpcCategoryModel rpcGetById(Long id) {
//        CategoryModel categoryModel = this.getById(id);
//        return BeanUtil.copyProperties(categoryModel, RpcCategoryModel.class);
//    }
}
